//old #define PCIE_BASE         0x000000
#define PCIE_BASE         0x0000140000000
#define CTRL_BASE         PCIE_BASE+0x000000
#define CNCR_BASE         PCIE_BASE+0x078000
#define PNCR_BASE         PCIE_BASE+0x07C000
#define PHY_BASE          PCIE_BASE+0x080000
#define ADDR_CTRL_0       0x0000    // Base address for CNCR/PNCR CTRL_0 register
#define ADDR_INTEN_0      0x0040    // Base address for CNCR/PNCR INTEN_0 register
#define ADDR_INTR_0       0x0050    // Base address for CNCR/PNCR INTR_0 register
#define ADDR_INTMSK_0     0x0060    // Base address for CNCR/PNCR INTMSK_0 register
#define ADDR_STS_0        0x0080    // Base address for CNCR/PNCR STS_0 register
#define ADDR_CTRL_NPHY_0  0x0200    // Base address for CNCR/PNCR CTRL_NPHY_0 register
#define ADDR_CTRL_NL_0    0x1000    // Base address for CNCR/PNCR CTRL_NL_0 register
#define ADDR_CTRL_NF_0    0x1800    // Base address for CNCR/PNCR CTRL_NF_0 register
#define ADDR_STS_NPHY_0   0x0300    // Base address for CNCR/PNCR STS_NPHY_0 register
#define ADDR_STS_NL_0     0x2000    // Base address for CNCR/PNCR STS_NL_0 register
#define ADDR_STS_NF_0     0x2800    // Base address for CNCR/PNCR STS_NF_0 register
#define NL                16
#define NF                1
#define NPHY              4

#define reg32_read(addr, rdata) \
	rdata = *((volatile unsigned int *)(addr));

#define reg32_write(addr, wdata) \
	*((volatile unsigned int *)(addr)) = wdata;

volatile unsigned int delays;
#define DELAY(delay_value) \
delays = delay_value*4;		\
while(delays--);

void reg32_read_exp(long long addr, int expdat, int expmask) {
    int rdata;
    reg32_read(addr,rdata);
    if ((rdata & expmask) != (expdat & expmask)) {
       printf("reg32_read_exp: Compare failed  Addr %x, Exp Data %x, Act Data %x, Exp Mask %x\r\n",addr, expdat, rdata, expmask);
    }
}

void reg32_read_write_check_rw(long long addr) {
    int temp_reg_rdata;
    reg32_read(addr, temp_reg_rdata);
    reg32_write(addr, 0x55555555);
    reg32_read_exp(addr, 0x55555555, 0xFFFFFFFF);
    reg32_write(addr, 0xffffffff);
    reg32_read_exp(addr, 0xffffffff, 0xFFFFFFFF);
    reg32_write(addr, 0xcccccccc);
    reg32_read_exp(addr, 0xcccccccc, 0xFFFFFFFF);
    reg32_write(addr, 0x33333333);
    reg32_read_exp(addr, 0x33333333, 0xFFFFFFFF);
    reg32_write(addr, 0x12345678);
    reg32_read_exp(addr, 0x12345678, 0xFFFFFFFF);
    reg32_write(addr, 0x87654321);
    reg32_read_exp(addr, 0x87654321, 0xFFFFFFFF);
    reg32_write(addr, temp_reg_rdata);
    reg32_read_exp(addr, temp_reg_rdata, 0xFFFFFFFF);
}
void reg32_write_mask(long long addr, int wdata, int wmask){
    int wdata_new;
    int rdata;
    reg32_read(addr, rdata);
    wdata_new = (wdata & wmask) | (rdata & ~wmask);
    reg32_write(addr, wdata_new);
}

void reg32clrbit(long long addr, int bitoffset){
   reg32_write_mask(addr, 0<<bitoffset, 1<<bitoffset);
}

void reg32_read_poll(long long addr, int expdat, int expmask, int loop_num) {
    int rdata;
    for (int i=0;i<loop_num;i++) {
        reg32_read(addr,rdata);
        if ((rdata & expmask) == (expdat & expmask)) {
           i = loop_num;
        }
//        //zqh for debug
//        reg32_read_exp(PHY_BASE+0x0, 0x74cd74cd, 0xffffffff);
//        reg32_read(PHY_BASE+0x20b0*4,rdata);
//        reg32_read(PHY_BASE+0x22b0*4,rdata);

        //delay(5us); //should use local delay function
        DELAY(102);
    }
    if ((rdata & expmask) != (expdat & expmask)) {
        printf("reg32_read_poll error: Compare failed  Addr %x, Exp Data %x, Exp Mask %x, LoopNum %d\r\n",addr, expdat, expmask, loop_num);
    }
}

void dut_pcie_phy_ini(int ref_clk_sel) {
    for (int i=0;i<NPHY;i=i+1) {
        reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + 0x10 + i * 0x04 /* PNCR_CTRL_NPHY_1.PHY_CR_PARA_SEL */, 0x1<<9, 0x1<<9); // bit 9
        reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_LANE_CLK_EN */, 0x1<<2, 0x1<<2); // bit 2
        reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_LANE_CLK_EN */, 0x1<<18, 0x1<<18); // bit 18

        if (ref_clk_sel==0) {
            // All PHY0~PHY3 are Internal ref Clock - i_soc_ref_alt_clk_p
            reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_CLK_SEL */, 0x0<<0, 0x3<<0); // bit 1~0
            reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_CLK_SEL */, 0x0<<16, 0x3<<16); // bit 17~16
            reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_ALT0_CLK_EN */, 0x1<<28, 0x1<<28); // bit 28
            reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_EN */, 0x0<<29, 0x1<<29); // bit 29
            reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_SEL */, 0x1<<30, 0x3<<30); // bit 31~30

        } else if (ref_clk_sel==1) {
            // PHY0 is phy0_ref_pad_clk
            // PHY1 is phy0.ref_repeat_clk_p/m
            // PHY2 is phy1.ref_repeat_clk_p/m
            // PHY3 is phy2.ref_repeat_clk_p/m
            if (i==0) {
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_CLK_SEL */, 0x1<<0, 0x3<<0);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_CLK_SEL */, 0x1<<16, 0x3<<16);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_ALT0_CLK_EN */, 0x0<<28, 0x1<<28);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_EN */, 0x1<<29, 0x1<<29);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_SEL */, 0x1<<30, 0x3<<30);
            } else if (i==3) {
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_CLK_SEL */, 0x2<<0, 0x3<<0);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_CLK_SEL */, 0x2<<16, 0x3<<16);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_ALT0_CLK_EN */, 0x0<<28, 0x1<<28);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_EN */, 0x0<<29, 0x1<<29);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_SEL */, 0x1<<30, 0x3<<30);
            } else {
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_CLK_SEL */, 0x2<<0, 0x3<<0);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_CLK_SEL */, 0x2<<16, 0x3<<16);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_ALT0_CLK_EN */, 0x0<<28, 0x1<<28);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_EN */, 0x1<<29, 0x1<<29);
                reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_SEL */, 0x2<<30, 0x3<<30);
            }
        } else {
           // All PHY0~PHY3 are ref pad Clock - phy*_ref_pad_clk_p/m
           reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFA_CLK_SEL */, 0x1<<0, 0x3<<0);
           reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REFB_CLK_SEL */, 0x1<<16, 0x3<<16);
           reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_ALT0_CLK_EN */, 0x0<<28, 0x1<<28);
           reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_EN */, 0x0<<29, 0x1<<29);
           reg32_write_mask(PNCR_BASE + ADDR_CTRL_NPHY_0 + i * 0x04 /* PNCR_CTRL_NPHY_0.PHY_REF_REPEAT_CLK_SEL */, 0x1<<30, 0x3<<30);
       }
    }

    // PHY_RESET = 0x0
    reg32clrbit(PNCR_BASE + ADDR_CTRL_0 + 0x00 /* PNCR_CTRL_0.PHY_RESET */, 0);

    //delay(1us); //should use local delay function
    DELAY(40);
    reg32_write_mask(PNCR_BASE + ADDR_CTRL_0 /* PNCR_CTRL_0.PHY_CR_PARA_CLK_BYPASS */, 0x0<<9, 0x1<<9); // bit 9 phy_cr_para_clk_bypass

    // APP_HOLD_PHY_RST = 0x0
    reg32clrbit(CNCR_BASE + ADDR_CTRL_0 + 0x00 /* CNCR_CTRL_0.APP_HOLD_PHY_RST */, 1);

    // Wait PHY*_PLLA_STATE=0x1
    for (int i=0;i<NPHY;i=i+1) {
        reg32_read_poll(PNCR_BASE + + ADDR_STS_NPHY_0 /* PNCR_STS_PHY_0 */, 0x1, 0x1,10000);
    }

}

//tmp void main() {
//tmp     dut_pcie_phy_ini(1);
//tmp     reg32_read_write_check_rw(CNCR_BASE + ADDR_CTRL_0 + 0x04/*CTRL0*/);
//tmp     reg32_read_write_check_rw(PNCR_BASE + ADDR_CTRL_0 + 0x04/*CTRL0*/);
//tmp     reg32_read_write_check_rw(CTRL_BASE+0x00000814 /*PORT_LOGIC.PHY_CONTROL_OFF*/);
//tmp     reg32_read_write_check_rw(PHY_BASE+ 0x0b * 4 /*SUP_DIG_MPLLA_BW_HIGH_OVRD_IN*/);
//tmp     reg32_read_write_check_rw(PHY_BASE+ 0x00040000 + 0x0b * 4 /*SUP_DIG_MPLLA_BW_HIGH_OVRD_IN*/);
//tmp }

